<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>ChatGpt</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">

    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
    <script src="//code.jquery.com/jquery.min.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.2/font/bootstrap-icons.css">

</head>

<body>

    <div class="container-fluid">
        <!-- 全屏拉满 -->
        <nav class="navbar bg-light mb-2">
            ChatGPT
        </nav>
        <div class="row">
            <!-- 放提示 -->
            <div aria-live="polite" aria-atomic="true" class="position-relative ">
                <!-- Position it: -->
                <!-- - `.toast-container` for spacing between toasts -->
                <!-- - `top-0` & `end-0` to position the toasts in the upper right corner -->
                <!-- - `.p-3` to prevent the toasts from sticking to the edge of the container  -->
                <div class="toast-container p-3  top-0 end-0">

                    <!-- Then put toasts within -->

                </div>
            </div>
        </div>
        <div class="row">
            <!-- 放model -->
            <div class="modal-container col">
                <!-- 放多个modal -->
            </div>
        </div>
        <!-- 主内容区 -->
        <div class="container-md">
            <!--
            md 手机会拉满，ipad不会，电脑不会。
            -->

            <div class="row mb-3">
                <div class="col-4 col-md-2">
                    <a href="javascript:;;" class="btn btn-primary bi bi-arrow-clockwise show-reset" style="font-size: 20px;" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="重置页面"></a>
                </div>
            </div>


            <div class="row" id="content">

            </div>
            <div class="row">

            </div>
        </div>

    </div>

    <script>
        document.body.onload = function() {
            function formtojson(data, that) {
                var newdata = {}
                for (const key in data) {
                    if (Object.hasOwnProperty.call(data, key)) {
                        const element = data[key];
                        // console.log(element, '-'.repeat(3));
                        // {name: 'examplemultiselect', value: '4'} '---'
                        dkey = element['name']
                        dval = element['value']
                        if (!(dkey in newdata)) {
                            //判断key是否属于多选框或checkbox ?
                            if ($(that).find(`[name=${dkey}]`).prop('multiple') || ($(that).find(`[name=${dkey}]`).length > 1 && $(that).find(`[name=${dkey}]`).prop('type') !== "radio")) {
                                // 数据类型可以暂时不考虑，后端 serializer会自动转换类型
                                newdata[dkey] = dval ? [dval] : []
                            } else {
                                newdata[dkey] = dval
                            }
                        } else {
                            newdata[dkey].push(dval)
                        }
                    }
                }
                return newdata
            }

            function convertCheckDataToHtml(typeData) {
                // 生成form
                let result = '<form>'
                for (key of Object.keys(typeData)) {
                    switch (typeData[key].type) {
                        case 'int':
                            result += `
                                <div class="input-group mb-3 mt-1">
                                    <input type="number" class="form-control" placeholder="${typeData[key].placeholder}" aria-describedby="request" name="${key}" value="${typeData[key].default}">
                                </div>
                            `
                            break;
                        default:
                            break;
                    }
                }
                result += '</form>'
                return result
            }
            // TODO: 测试
            window.getModal = async function(typeData, title, onclick) {
                result = convertCheckDataToHtml(typeData)

                const template = `
                            <!-- Modal -->
                            <div class="modal fade"  tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
                            <div class="modal-dialog">
                                <div class="modal-content">
                                <div class="modal-header">
                                    <h1 class="modal-title fs-5" id="exampleModalLabel">${title}</h1>
                                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                                </div>
                                <div class="modal-body">
                                    ${result}
                                </div>
                                <div class="modal-footer">
                                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                                    <button type="button" class="btn btn-primary" >Save changes</button>
                                </div>
                                </div>
                            </div>
                            </div>
                            `
                const newdiv = document.createElement('div')
                newdiv.innerHTML = template
                document.querySelector('.modal-container').appendChild(newdiv)
                const modal = new bootstrap.Modal(newdiv.children.item(0))
                modal.show()

                // 当前div中的save进行添加点击事件
                const save_btn = newdiv.getElementsByClassName('modal-footer').item(0).getElementsByTagName('button').item(1)
                save_btn.addEventListener('click', onclick(modal, typeData))

                // 当关闭时，触发销毁 modal
                function hidenModal(modal, newdiv) {
                    function wrapper(event) {
                        console.log(event)
                        console.log(modal)
                        console.log(newdiv)
                        console.log('close')
                            // 删除
                        document.querySelector('.modal-container').removeChild(newdiv)
                    }
                    return wrapper
                }
                newdiv.children.item(0).addEventListener('hide.bs.modal', hidenModal(modal, newdiv))

                return modal
            }

            function CheckRules(key, typeData, val) {
                // key: count , typeData[key].rules: [{required: true, min:1, max:5}, val: 1}]
                let rules = typeData[key].rules;

                const default_val = typeData[key].default;

                for (rule of rules) {
                    const type = rule.type
                    let required = rule.required
                    if (required !== false && required !== true) {
                        required = true
                    }

                    if (!val) {
                        if (required) {
                            return default_val
                        } else {
                            if (default_val) {
                                return default_val
                            } else {
                                getToast('warning', `${key}的规则验证不通过`, `值: ${val}, 规则: ${JSON.stringify(rule)}`)
                                return ''
                            }
                        }
                    }
                    switch (type) {
                        case 'range':
                            min = rule.min ? rule.min : 0
                            max = rule.max ? rule.max : 1
                            if (max >= val >= min) {
                                return val
                            } else {
                                getToast('warning', `${key}的规则验证不通过`, `值: ${val}, 规则: ${JSON.stringify(rule)}`)
                            }
                            break;
                            // 这里扩展其他类型的规则验证
                        default:
                            break;
                    }
                }
            }
            // 类型转值
            function convertDataFromType(typeData, data) {
                const newdata = {}
                for (key of Object.keys(data)) {
                    if (key in typeData) {
                        let val
                        switch (typeData[key].type) {
                            case "int":
                                val = parseInt(data[key])
                                break;
                            case "float":
                                val = parseFloat(data[key])
                                break;
                            default:
                                val = new String(data[key])
                                break;
                        }
                        // 对转换后的值进行检验
                        val = CheckRules(key, typeData, val)
                        newdata[key] = val
                    }
                }
                return newdata
            }


            function getToast(color, title, msg) {
                const template = `
                    <div  class="toast  text-bg-${color} " role="alert" aria-live="assertive" aria-atomic="true">
                        <div class="toast-header">
                            <strong class="me-auto">${title}</strong>
                            <small class="text-muted">just now</small>
                            <button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast" aria-label="Close"></button>
                        </div>
                        <div class="toast-body">
                            ${msg}
                        </div>
                    </div>
                    `
                const newdiv = document.createElement('div')
                newdiv.innerHTML = template
                const toast_container = document.querySelector('.toast-container')
                toast_container.appendChild(newdiv)
                const toast = new bootstrap.Toast(newdiv.children.item(0))
                toast.show()

                // 当关闭时，触发销毁 modal
                function hidenToast(toast, newdiv) {
                    function wrapper(event) {
                        // 删除
                        document.querySelector('.toast-container').removeChild(newdiv)
                    }
                    return wrapper
                }
                newdiv.children.item(0).addEventListener('hidden.bs.toast', hidenToast(toast, newdiv))

                return toast
            }

            function getcard(int) {
                return `
                <div class="card mb-3 col-12 col-md-12">
                    <div class="card-body">
                        <button class="btn btn-outline-secondary bi bi-airplane request-button" type="button" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="发请求">
                             <!-- visually-hidden -->
                            <span class="spinner-grow spinner-grow-sm visually-hidden" role="status" aria-hidden="true"></span>
                        </button>
                        <button class="btn btn-outline-secondary bi bi-plus-lg request-add-button" type="button" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="添加一个新的节点"></button>
                        <button class="btn btn-outline-secondary bi bi-clipboard request-copy-button" type="button" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="复制一个问题"></button>
                        <button class="btn btn-outline-secondary bi bi-trash3-fill request-del-button" type="button" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="删除一个节点"></button>
                        <button class="btn btn-outline-secondary bi bi-bookmark-star request-star-button" type="button" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="精选节点"></button>
                        <div class="input-group mb-3 mt-1">
                            <textarea type="text" class="form-control request-input" placeholder="输入问题" aria-describedby="request"></textarea>
                            <!-- visually-hidden -->
                            <span class="spinner-grow spinner-grow-sm visually-hidden" role="status" aria-hidden="true"></span>
                            </button>
                            </button>
                        </div>

                        <div class="card-text" style="white-space: pre-line;   font-size: 16px;">

                        </div>
                    </div>

                </div>
`
            }



            function show_result(uuid, current_button, text) {
                let intervalid = setInterval(async function() {
                        // let response = await fetch(`http://localhost:8000/chatgpt/${uuid}/`, {
                        //     method: 'GET',
                        //     headers: {
                        //         'Accept': 'application/json',
                        //         'Content-Type': 'application/json'
                        //     }
                        // })

                        let response = await http(`chatgpt/${uuid}/`, 'GET', {
                                'Accept': 'application/json',
                                'Content-Type': 'application/json'
                            }, undefined,
                            undefined,
                            function(response) {

                                console.log(`获取到${uuid}的状态${response.state}`)
                                if (response.state === "fi") {

                                    text.innerHTML = response.ans
                                    getToast('success', 'Information', '请求成功.')
                                        // 请求完了，用户可以继续点。加载状态取消
                                    current_button.removeAttribute('disabled');
                                    current_button.getElementsByTagName('span').item(0).classList.add('visually-hidden')
                                        // 后台处理中断
                                    clearInterval(intervalid)

                                } else if (response.state === "er") {
                                    getToast("danger", "Error", response.ans)
                                        // 请求完了，用户可以继续点。加载状态取消
                                    current_button.removeAttribute('disabled');
                                    current_button.getElementsByTagName('span').item(0).classList.add('visually-hidden')
                                        // 后台处理中断
                                    clearInterval(intervalid)

                                }

                            }
                        )


                    },
                    5000)

            }

            function addNewDiv(index) {
                let div = document.createElement('div')
                div.innerHTML = getcard(index)
                div.setAttribute('id', index)
                div.setAttribute('class', 'row')
                return div
            }


            function clickOneReset(event) {
                event.preventDefault()
                document.content.innerHTML = ''
                let index = 0
                document.content.append(addNewDiv(index++))
                document.content.append(addNewDiv(index++))
                document.content.append(addNewDiv(index++))

                addClickToNeededBtn()
            }

            function getParentEle(ele, selector) {
                let parentEle
                do {
                    parentEle = ele.parentElement
                    console.log(`当前ele ${ele}获取到父node ${parentEle}`)



                    if (parentEle.matches(selector)) {
                        return parentEle
                    } else {
                        ele = parentEle
                    }
                } while (true)
                return parentEle
            }
            async function clickAddBtn(event) {
                event.preventDefault()
                let current_button = event.target
                let current_div = getParentEle(current_button, 'div.row')
                let text = current_div.getElementsByClassName('card-text').item(0)
                let current_input = current_div.getElementsByClassName('request-input')[0]

                let length = Array.from(document.content.children).length

                let current_index = Array.from(document.content.children).indexOf(current_div)

                // 如果当前不是最后一个div
                console.log({
                    length,
                    current_index
                })
                if (length !== current_index) {

                    document.content.insertBefore(addNewDiv(length), current_div.nextElementSibling)
                } else {
                    document.content.appendChild(addNewDiv(length))
                }

                addClickToNeededBtn()
            }

            async function clickStarBtn(event) {

                event.preventDefault()
                let current_button = event.target
                let current_div = getParentEle(current_button, 'div.row')
                let text = current_div.getElementsByClassName('card-text').item(0)
                let current_input = current_div.getElementsByClassName('request-input')[0]
                let current_index = Array.from(document.content.children).indexOf(current_div)
                let current_star = current_div.querySelector('.request-star-button')
                    // 当前背景加色
                if (!current_div.querySelector('div.card').style.cssText) {
                    // 空变存在
                    current_div.querySelector('div.card').style.cssText = 'background-color: rgb(229, 239, 232);'
                        // 图标也需要变化
                    current_star.classList.remove('bi-bookmark-star')
                    current_star.classList.add('bi-bookmark-star-fill')
                } else {
                    // 存在变空
                    current_div.querySelector('div.card').style.cssText = ''
                        // 图标变
                    current_star.classList.remove('bi-bookmark-star-fill')
                    current_star.classList.add('bi-bookmark-star')
                }
            }

            async function clickDelBtn(event) {
                if (!confirm('删除吗? ')) {
                    return
                }

                event.preventDefault()
                let current_button = event.target
                let current_div = getParentEle(current_button, 'div.row')
                let text = current_div.getElementsByClassName('card-text').item(0)
                let current_input = current_div.getElementsByClassName('request-input')[0]
                let current_index = Array.from(document.content.children).indexOf(current_div)
                if (Array.from(document.content.children).length !== 1) {
                    document.content.removeChild(current_div)
                } else {
                    getToast('warning', 'Information', '至少要保留一个节点.')
                }
                addClickToNeededBtn()
            }
            async function clickCopyBtn(event) {
                event.preventDefault()
                let current_button = event.target
                let current_div = getParentEle(current_button, 'div.row')
                let text = current_div.getElementsByClassName('card-text').item(0)
                let current_input = current_div.getElementsByClassName('request-input')[0]
                if (!current_input.value) {
                    console.log('current_input empty value, 没有效果')

                    getToast('danger', 'Error ~~~', 'empty value, 不能执行')
                    addClickToNeededBtn()
                    return
                }


                getModal({
                    count: {
                        type: 'int',
                        placeholder: '输入次数哦',
                        default: 1,
                        rules: [{
                            max: 5,
                            min: 1,
                            type: 'range'
                        }]
                    }
                }, '复制几次呢？', function(input_modal, typeData) {
                    function wrapper(event) {
                        const form = getParentEle(event.target, 'div.modal-dialog').querySelector('form')
                        const modal = getParentEle(event.target, 'div.modal')
                        input_modal.hide()
                        const data = formtojson($(form).serializeArray(), form)
                            // console.log(convertDataFromType(typeData, data)) // { count: 1 }
                        const ret_data = convertDataFromType(typeData, data)


                        for (i of new Array(ret_data.count)) {
                            let length = Array.from(document.content.children).length

                            let current_index = Array.from(document.content.children).indexOf(current_div)

                            // 如果当前不是最后一个div
                            console.log({
                                length,
                                current_index
                            })
                            newdiv = addNewDiv(length)
                            if (length !== current_index) {

                                document.content.insertBefore(newdiv, current_div.nextElementSibling)
                            } else {
                                document.content.appendChild(newdiv)
                            }

                            // 给新的button事件， new_button.click()
                            addClickToNeededBtn()

                            // 新的div中的 input内容应该是
                            let new_input = newdiv.getElementsByClassName('request-input')[0]
                            let new_button = newdiv.getElementsByClassName('request-button').item(0)
                            console.log({
                                new_input,
                                new_button,
                                current_input
                            })

                            oldvalue = current_input.value
                            new_input.value = oldvalue
                            new_button.click()

                            // 所有button重载事件
                            addClickToNeededBtn()
                        }
                    }
                    return wrapper
                })


            }

            async function clickOneBtn(event) {
                event.preventDefault()
                let current_button = event.target
                let current_div = getParentEle(current_button, 'div.row')
                let text = current_div.getElementsByClassName('card-text').item(0)
                let current_input = current_div.getElementsByClassName('request-input')[0]
                console.log({
                    current_div,
                    current_button,
                    text,
                    current_input
                })

                event.preventDefault();
                console.log('click button', current_input.value)
                if (!current_input.value) {
                    console.log('current_input empty value, 没有效果')
                    getToast('danger', 'Error ~~~', 'empty value, 不能执行')
                    addClickToNeededBtn()
                    return
                }

                // let response = await fetch('http://localhost:8000/chatgpt/', {
                //     method: 'POST',
                //     headers: {
                //         'Accept': 'application/json',
                //         'Content-Type': 'application/json'
                //     },
                //     body: JSON.stringify({
                //         req: current_input.value
                //     })
                // })

                let response = await http('chatgpt/', 'POST', {
                        'Accept': 'application/json',
                        'Content-Type': 'application/json'
                    },
                    JSON.stringify({
                        req: current_input.value
                    }),
                    function() {
                        // 加载时，就显示加载状态
                        current_button.getElementsByTagName('span').item(0).classList.remove('visually-hidden')
                            // 加载时，就让button不可以点，避免用户多次点击
                        current_button.setAttribute('disabled', true)
                    },
                    function(response) {
                        let {
                            uuid,
                            state
                        } = response
                        if (state === "pr") {
                            // 后台处理中
                            show_result(uuid, current_button, text)
                        }
                    }
                )
            }
            async function http(path, method, headers, data, loadingCallback, successCallback) {
                console.warn({
                    path,
                    method,
                    headers,
                    data,
                    loadingCallback,
                    successCallback
                })

                let response = await fetch(`${BASEURL}/${path}`, {
                    method,
                    headers,
                    body: data
                })
                console.warn({
                    response
                }, '拿到的响应')
                if (loadingCallback) {
                    loadingCallback()
                }
                response = await response.json()
                console.warn({
                    response
                }, '成功或失败的响应')
                if (successCallback) {
                    successCallback(response)
                }
                return response
            }

            function addEventsToEles(eles, fn, action) {
                for (ele of eles) {
                    ele.addEventListener(action, fn)

                }
            }

            function addEventsToInps(eles) {
                for (input of eles) {
                    input.addEventListener("keypress", function(event) {
                        if (event.key === "Enter") {
                            event.preventDefault();
                            let current_div = getParentEle(event.target, 'div.row')
                            let current_button = current_div.getElementsByClassName('request-button').item(0)
                            current_button.click()
                        }
                    });
                }
            }

            function disposeTooltip() {
                // 如果有就先销毁
                if (tooltipList) {
                    tooltipList.forEach(item => {
                        if (item) {
                            item.dispose()
                        }
                    })
                }
            }

            function addClickToNeededBtn() {
                // 新dom出现均需要添加点击事件

                // 请求
                addEventsToEles(buttons, clickOneBtn, 'click')
                    // 其后添加1个新 addEventsToInps(inputs)
                addEventsToEles(adds, clickAddBtn, 'click')
                    // 其后复制一个新的
                addEventsToEles(copys, clickCopyBtn, 'click')
                    // 删除一个节点
                addEventsToEles(dels, clickDelBtn, 'click')
                    // 精选节点
                addEventsToEles(stars, clickStarBtn, 'click')


                // 生成提示
                const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
                    // 销毁
                disposeTooltip()
                    // 生成
                tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => {
                    if (tooltipTriggerEl) {
                        return new bootstrap.Tooltip(tooltipTriggerEl)
                    }
                })

            }
            console.log('start ...');

            // 全局属性

            var BASEURL = 'http://chatgpt.mykernel.cn'

            var tooltipList = []

            document.content = document.getElementById('content');
            const showNexts = document.getElementsByClassName('show-next')
            const showReset = document.getElementsByClassName('show-reset')

            // 初始化3个盒子
            let index = 0
            document.content.append(addNewDiv(index++));
            document.content.append(addNewDiv(index++));
            document.content.append(addNewDiv(index++))

            // button click 提交
            const buttons = document.getElementsByClassName('request-button')
            const inputs = document.getElementsByClassName('request-input')
            const adds = document.getElementsByClassName('request-add-button')
            const copys = document.getElementsByClassName('request-copy-button')
            const dels = document.getElementsByClassName('request-del-button')
            const stars = document.getElementsByClassName('request-star-button')


            addClickToNeededBtn();
            addEventsToEles(showReset, clickOneReset, 'click')



        }
    </script>
    <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>

</body>

</html>
